home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
news
/
inn1.000
/
inn1.4sec-linux-src.tar
/
inn
/
backends
/
buffchan.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-03-18
|
11KB
|
540 lines
/* $Revision: 1.9 $
**
** Buffered file exploder for innd.
*/
#include "configdata.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <ctype.h>
#include <errno.h>
#include "paths.h"
#include "libinn.h"
#include "clibrary.h"
#include "qio.h"
#include "macros.h"
extern void MAPread();
extern char *MAPname();
/*
** Hash functions for hashing sitenames.
*/
#define SITE_HASH(Name, p, j) \
for (p = Name, j = 0; *p; ) j = (j << 5) + j + *p++
#define SITE_SIZE 128
#define SITE_BUCKET(j) &SITEtable[j & (SITE_SIZE - 1)]
/*
** Entry for a single active site.
*/
typedef struct _SITE {
BOOL Dropped;
STRING Name;
int CloseLines;
int FlushLines;
time_t LastFlushed;
time_t LastClosed;
int CloseSeconds;
int FlushSeconds;
FILE *F;
STRING Filename;
char *Buffer;
} SITE;
/*
** Site hashtable bucket.
*/
typedef struct _SITEHASH {
int Size;
int Used;
SITE *Sites;
} SITEHASH;
/* Global variables. */
STATIC char *Format;
STATIC STRING Map;
STATIC int BufferMode;
STATIC int CloseEvery;
STATIC int FlushEvery;
STATIC int CloseSeconds;
STATIC int FlushSeconds;
STATIC SIGVAR GotInterrupt;
STATIC SITEHASH SITEtable[SITE_SIZE];
STATIC TIMEINFO Now;
#if defined(DONT_HAVE_FCHMOD)
/*
** A dummy fchmod.
*/
/* ARGSUSED */
int
fchmod(i)
int i;
{
}
#endif /* defined(DONT_HAVE_FCHMOD) */
/*
** Set up the site information. Basically creating empty buckets.
*/
STATIC void
SITEsetup()
{
register SITEHASH *shp;
for (shp = SITEtable; shp < ENDOF(SITEtable); shp++) {
shp->Size = 3;
shp->Sites = NEW(SITE, shp->Size);
shp->Used = 0;
}
}
/*
** Close a site
*/
STATIC void
SITEclose(sp)
register SITE *sp;
{
register FILE *F;
if ((F = sp->F) != NULL) {
if (fflush(F) == EOF || ferror(F)
|| fchmod((int)fileno(F), 0664) < 0
|| fclose(F) == EOF)
(void)fprintf(stderr, "buffchan %s cant close %s, %s\n",
sp->Name, sp->Filename, strerror(errno));
sp->F = NULL;
}
}
/*
** Close all open sites.
*/
STATIC void
SITEcloseall()
{
register SITEHASH *shp;
register SITE *sp;
register int i;
for (shp = SITEtable; shp < ENDOF(SITEtable); shp++)
for (sp = shp->Sites, i = shp->Used; --i >= 0; sp++)
SITEclose(sp);
}
/*
** Open the file for a site.
*/
STATIC void
SITEopen(sp)
register SITE *sp;
{
int e;
if ((sp->F = xfopena(sp->Filename)) == NULL
&& ((e = errno) != EACCES || chmod(sp->Filename, 0644) < 0
|| (sp->F = xfopena(sp->Filename)) == NULL)) {
(void)fprintf(stderr, "buffchan %s cant fopen %s, %s\n",
sp->Name, sp->Filename, strerror(e));
if ((sp->F = fopen("/dev/null", "w")) == NULL) {
/* This really should not happen. */
(void)fprintf(stderr, "buffchan %s cant fopen %s, %s\n",
sp->Name, "/dev/null", strerror(errno));
exit(1);
}
}
else if (fchmod((int)fileno(sp->F), 0444) < 0)
(void)fprintf(stderr, "buffchan %s cant fchmod %s %s\n",
sp->Name, sp->Filename, strerror(errno));
if (BufferMode != '\0')
setbuf(sp->F, sp->Buffer);
/* Reset all counters. */
sp->FlushLines = 0;
sp->CloseLines = 0;
sp->LastFlushed = Now.time;
sp->LastClosed = Now.time;
sp->Dropped = FALSE;
}
/*
** Find a site, possibly create if not found.
*/
STATIC SITE *
SITEfind(Name, CanCreate)
char *Name;
BOOL CanCreate;
{
register char *p;
register int i;
unsigned int j;
register SITE *sp;
SITEHASH *shp;
char c;
char buff[BUFSIZ];
/* Look for site in the hash table. */
/* SUPPRESS 6 *//* Over/underflow from plus expression */
SITE_HASH(Name, p, j);
shp = SITE_BUCKET(j);
for (c = *Name, sp = shp->Sites, i = shp->Used; --i >= 0; sp++)
if (c == sp->Name[0] && caseEQ(Name, sp->Name))
return sp;
if (!CanCreate)
return NULL;
/* Adding a new site -- grow hash bucket if we need to. */
if (shp->Used == shp->Size - 1) {
shp->Size *= 2;
RENEW(shp->Sites, SITE, shp->Size);
}
sp = &shp->Sites[shp->Used++];
/* Fill in the structure for the new site. */
sp->Name = COPY(Name);
(void)sprintf(buff, (STRING)Format, Map ? MAPname(Name) : sp->Name);
sp->Filename = COPY(buff);
if (BufferMode == 'u')
sp->Buffer = NULL;
else if (BufferMode == 'b')
sp->Buffer = NEW(char, BUFSIZ);
SITEopen(sp);
return sp;
}
/*
** Flush a site -- close and re-open the file.
*/
STATIC void
SITEflush(sp)
register SITE *sp;
{
register FILE *F;
if ((F = sp->F) != NULL) {
if (fflush(F) == EOF || ferror(F)
|| fchmod((int)fileno(F), 0664) < 0
|| fclose(F) == EOF)
(void)fprintf(stderr, "buffchan %s cant close %s, %s\n",
sp->Name, sp->Filename, strerror(errno));
sp->F = NULL;
}
if (!sp->Dropped)
SITEopen(sp);
}
/*
** Flush all open sites.
*/
STATIC void
SITEflushall()
{
register SITEHASH *shp;
register SITE *sp;
register int i;
for (shp = SITEtable; shp < ENDOF(SITEtable); shp++)
for (sp = shp->Sites, i = shp->Used; --i >= 0; sp++)
SITEflush(sp);
}
/*
** Write data to a site.
*/
STATIC void
SITEwrite(name, text, len)
register char *name;
register char *text;
register int len;
{
register SITE *sp;
sp = SITEfind(name, TRUE);
if (sp->F == NULL)
SITEopen(sp);
if (fwrite((POINTER)text, (SIZE_T)1, (SIZE_T)len, sp->F) != len)
(void)fprintf(stderr, "buffchan %s cant write %s\n",
sp->Name, strerror(errno));
/* Bump line count; see if time to close or flush. */
if (CloseEvery && ++(sp->CloseLines) >= CloseEvery) {
SITEflush(sp);
return;
}
if (CloseSeconds && sp->LastClosed + CloseSeconds < Now.time) {
SITEflush(sp);
return;
}
if (FlushEvery && ++(sp->FlushLines) >= FlushEvery) {
if (fflush(sp->F) == EOF || ferror(sp->F))
(void)fprintf(stderr, "buffchan %s cant flush %s, %s\n",
sp->Name, sp->Filename, strerror(errno));
sp->LastFlushed = Now.time;
sp->FlushLines = 0;
}
else if (FlushSeconds && sp->LastFlushed + FlushSeconds < Now.time) {
if (fflush(sp->F) == EOF || ferror(sp->F))
(void)fprintf(stderr, "buffchan %s cant flush %s, %s\n",
sp->Name, sp->Filename, strerror(errno));
sp->LastFlushed = Now.time;
sp->FlushLines = 0;
}
}
/*
** Handle a command message.
*/
STATIC void
Process(p)
register char *p;
{
register SITE *sp;
if (*p == 'b' && EQn(p, "begin", 5))
/* No-op. */
return;
if (*p == 'f' && EQn(p, "flush", 5)) {
for (p += 5; ISWHITE(*p); p++)
continue;
if (*p == '\0')
SITEflushall();
else if ((sp = SITEfind(p, FALSE)) != NULL)
SITEflush(sp);
else
/*(void)fprintf(stderr, "buffchan flush %s unknown site\n", p);*/
;
return;
}
if (*p == 'd' && EQn(p, "drop", 4)) {
for (p += 4; ISWHITE(*p); p++)
continue;
if (*p == '\0')
SITEcloseall();
else if ((sp = SITEfind(p, FALSE)) == NULL)
(void)fprintf(stderr, "buffchan drop %s unknown site\n", p);
else {
SITEclose(sp);
sp->Dropped = TRUE;
}
return;
}
if (*p == 'r' && EQn(p, "readmap", 7)) {
MAPread(Map);
return;
}
/* Other command messages -- ignored. */
(void)fprintf(stderr, "buffchan unknown message %s\n", p);
}
/*
** Print usage message and exit.
*/
STATIC NORETURN
Usage()
{
(void)fprintf(stderr, "Usage error.\n");
exit(1);
}
/*
** Mark that we got a signal; let two signals kill us.
*/
STATIC SIGHANDLER
CATCHinterrupt(s)
int s;
{
GotInterrupt = TRUE;
(void)signal(s, SIG_DFL);
}
int
main(ac, av)
int ac;
char *av[];
{
static char BATCHDIR[] = _PATH_BATCHDIR;
register QIOSTATE *qp;
register int i;
register int Fields;
register char *p;
register char *next;
register char *line;
char *Directory;
BOOL Redirect;
FILE *F;
/* Set defaults. */
Directory = NULL;
Fields = 1;
Format = NULL;
Redirect = TRUE;
GotInterrupt = FALSE;
(void)umask(NEWSUMASK);
(void)signal(SIGHUP, CATCHinterrupt);
(void)signal(SIGINT, CATCHinterrupt);
(void)signal(SIGQUIT, CATCHinterrupt);
(void)signal(SIGPIPE, CATCHinterrupt);
(void)signal(SIGTERM, CATCHinterrupt);
(void)signal(SIGALRM, CATCHinterrupt);
/* Parse JCL. */
while ((i = getopt(ac, av, "bc:C:d:f:l:L:m:p:rs:u")) != EOF)
switch (i) {
default:
Usage();
/* NOTREACHED */
case 'b':
case 'u':
BufferMode = i;
break;
case 'c':
CloseEvery = atoi(optarg);
break;
case 'C':
CloseSeconds = atoi(optarg);
break;
case 'd':
Directory = optarg;
if (Format == NULL)
Format ="%s";
break;
case 'f':
Fields = atoi(optarg);
break;
case 'l':
FlushEvery = atoi(optarg);
break;
case 'L':
FlushSeconds = atoi(optarg);
break;
case 'm':
Map = optarg;
MAPread(Map);
break;
case 'p':
if ((F = fopen(optarg, "w")) == NULL) {
(void)fprintf(stderr, "buffchan cant fopen %s %s\n",
optarg, strerror(errno));
exit(1);
}
(void)fprintf(F, "%ld\n", (long)getpid());
if (ferror(F) || fclose(F) == EOF) {
(void)fprintf(stderr, "buffchan cant fclose %s %s\n",
optarg, strerror(errno));
exit(1);
}
break;
case 'r':
Redirect = FALSE;
break;
case 's':
Format = optarg;
break;
}
ac -= optind;
av += optind;
if (ac)
Usage();
/* Do some basic set-ups. */
if (Redirect)
(void)freopen(_PATH_ERRLOG, "a", stderr);
if (Format == NULL) {
Format = NEW(char, STRLEN(BATCHDIR) + 1 + 2 + 1);
(void)sprintf(Format, "%s/%%s", BATCHDIR);
}
if (Directory && chdir(Directory) < 0) {
(void)fprintf(stderr, "buffchan cant chdir %s %s\n",
Directory, strerror(errno));
exit(1);
}
SITEsetup();
/* Read input. */
for (qp = QIOfdopen((int)fileno(stdin), 0); !GotInterrupt ; ) {
if ((line = QIOread(qp)) == NULL) {
if (QIOerror(qp)) {
(void)fprintf(stderr, "buffchan cant read %s\n",
strerror(errno));
break;
}
if (QIOtoolong(qp)) {
(void)fprintf(stderr, "buffchan long_line");
(void)QIOread(qp);
continue;
}
/* Normal EOF. */
break;
}
/* Command? */
if (*line == EXP_CONTROL && *++line != EXP_CONTROL) {
Process(line);
continue;
}
/* Skip the right number of leading fields. */
for (i = Fields, p = line; *p; p++)
if (*p == ' ' && --i <= 0)
break;
if (*p == '\0')
/* Nothing to write. Probably shouldn't happen. */
continue;
/* Add a newline, get the length of all leading fields. */
*p++ = '\n';
i = p - line;
if (GetTimeInfo(&Now) < 0) {
(void)fprintf(stderr, "buffchan cant gettime %s\n",
strerror(errno));
break;
}
/* Rest of the line is space-separated list of filenames. */
for (; *p; p = next) {
/* Skip whitespace, get next word. */
while (*p == ' ')
p++;
for (next = p; *next && *next != ' '; next++)
continue;
if (*next)
*next++ = '\0';
SITEwrite(p, line, i);
}
}
SITEcloseall();
exit(0);
/* NOTREACHED */
}